package cate.game.role.pvp.gradepvp;

import cate.common.table.d.*;
import cate.common.table.pvp.gradepvp.row.GradePvpLevelRow;
import cate.common.table.pvp.gradepvp.row.GradePvpRewardRow;
import cate.common.util.GameResult;
import cate.game.mail.po.MailBuilder;
import cate.game.play.fighter.HeroFighter;
import cate.game.play.part.PlaySide;
import cate.game.play.proce.play.PlayPO;
import cate.game.play.vo.ReplayUtils;
import cate.game.pvp.gradepvp.GradePvpStatus;
import cate.game.pvp.gradepvp.dto.GradePvpPlayerData;
import cate.game.pvp.gradepvp.msg.GradePvpChallengeResp;
import cate.game.pvp.gradepvp.msg.GradePvpDataResp;
import cate.game.pvp.gradepvp.msg.GradePvpMatchResp;
import cate.game.res.MixRes;
import cate.game.res.MixResAdder;
import cate.game.role.Role;
import cate.game.role.farm.AbstractFarm;
import cate.game.role.farm.PlayTimes;
import cate.game.role.fight.Position;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import cp.solution.manage.api.open.replay.ReplaySaveReq;
import cp.solution.manage.api.open.replay.ReplaySaveResp;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.springframework.data.annotation.Transient;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

public class MyGradePvp extends AbstractFarm {
    @NoteField(value = "挑战次数")
    public PlayTimes playTimes;

    @NoteField(value = "当前的星数")
    public int star;

    @NoteField(value = "缓冲区积分")
    public int bufferScore;

    @NoteField("段位积分")
    public int score;

    @NoteField(value = "我的录像")
    @JsonIgnore
    public List<GradePvpRecord> records;

    @NoteField(value = "段位奖励领取状态")
    public Map<Integer, Boolean> gotMap;

    @NoteField(value = "匹配数据")
    public GradePvpMatchData match;

    @NoteField(value = "上半赛季是否已经领取过奖励")
    @JsonIgnore
    public boolean firstHalfSentReward;

    @NoteField(value = "下半赛季是否已经领取过奖励")
    @JsonIgnore
    public boolean secondHalfSentReward;

    @NoteField(value = "当前的赛季")
    public int mySeason;

    @NoteField(value = "总战绩")
    @JsonIgnore
    public GradePvpMark total;

    @NoteField(value = "当前赛季战绩")
    @JsonIgnore
    public GradePvpMark present;

    @Transient
    @JsonProperty
    public int myRank;

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (playTimes == null) {
            playTimes = new PlayTimes();
        }
        playTimes.initialize();
        if (records == null) {
            records = new ArrayList<>();
        }
        if (gotMap == null) {
            gotMap = new HashMap<>();
        }
        if (total == null) {
            total = new GradePvpMark();
        }
        total.initialize();
        if (present == null) {
            present = new GradePvpMark();
        }
        present.initialize();
    }

    @JsonProperty("rank")
    public int getRank() {
        try {
            myRank = game().pvp.gradePvp.getRank(role.getUid());
            if (myRank > -1) {
                if (myRank < total.rank) {
                    total.rank = myRank;
                }
                if (myRank < present.rank) {
                    present.rank = myRank;
                }
            }
            return myRank;
        } catch (Exception e) {

        }
        return -1;
    }

    @JsonProperty("seasonStartTime")
    public long getSeasonStartTime() {
        return game().pvp.gradePvp.getSeasonStartTime();
    }

    @Override
    public int getFuncId() {
        return GDFunc.GRADE_PVP;
    }

    @Override
    public void onAfterOpen() {

    }

    public void checkMatchAndStart() {
        try {
            if (match != null && !fighting) {
                // 再清除一遍战斗数据
                beginChallenge();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            match = null;
        }
    }

    @Override
    public void onEnter() {
        super.onEnter();
        checkSeason();
//        uninstallGeneralTeam();
    }

    public void uninstallGeneralTeam(){
        if(serverSeason() > 0){
            return;
        }
        Position position = role.getFight().getPosition(GDFunc.GRADE_PVP);
        if (position != null && !position.grids.isEmpty()) {
            role.getFight().removeFuncPosition(GDFunc.GRADE_PVP);
        }
    }

    @Override
    public boolean customizeCheckOpen() {
        return System.currentTimeMillis() >= serverSeasonStatus().startTime;
    }

    public void sendRewardByStar(boolean firstHalf) {
        logger.info(
                "开始结算段位赛奖励，赛季：S{}{}，玩家姓名：{}，uid：{}，玩家是否开启段位赛：{}，玩家星级：{}",
                mySeason,
                firstHalf ? "上半赛季" : "下半赛季",
                role.getBase().name,
                role.getUid(),
                isOpen(),
                star);
        if (isOpen()) {
            GradePvpRewardRow rewardRow = game().table.gradePvp.reward.getRowByStar(star);
            if (rewardRow != null) {
                List<Object> params = new ArrayList<>();
                params.add(rewardRow.gradeName);
                game().mail.send(role, new MailBuilder()
                        .setContentId(GDGradePvp.SETTLE_REWARD_MAIL_ID)
                        .setParamList(params)
                        .setReward(new MixRes(rewardRow.gradeRewardStr))
                        .setOperation(GDOperation.GRADE_PVP_SETTLE_REWARD)
                        .setOperationSub(star)
                        .build());
            }
        }
    }

    // 递归领奖并设置新的段位
    public synchronized void checkSeason() {
        if (isOpen()) {
            int serverSeason = serverSeason();
            // 如果赛季不一样,递归发奖励
            if (mySeason == 0) {
                if(mySeason < serverSeason){
                    clearData();
                }
                mySeason = serverSeason;
                firstHalfSentReward = serverSeasonStatus().firstHalfSentReward;
                secondHalfSentReward = serverSeasonStatus().secondHalfSentReward;
                return;
            }
            if (mySeason < serverSeason) {
                if (!firstHalfSentReward) {
                    sendRewardByStar(true);
                }
                if (!secondHalfSentReward) {
                    sendRewardByStar(false);
                    star = game().table.gradePvp.level.getInheritStarByStar(star);
                    // 将继承得星级赋值给战绩
                    present.star = star;
                    present.maxStar = star;
                }
                mySeason += 1;
                // 清除赛季数据
                clearData();
                checkSeason();
            } else if (mySeason == serverSeason) {
                if ((serverSeasonStatus().firstHalfSentReward && !firstHalfSentReward)) {
                    sendRewardByStar(true);
                    firstHalfSentReward = true;
                }
                if ((serverSeasonStatus().secondHalfSentReward && !secondHalfSentReward)) {
                    sendRewardByStar(false);
                    secondHalfSentReward = true;
                    star = game().table.gradePvp.level.getInheritStarByStar(star);
                    present.star = star;
                    present.maxStar = star;
                }
            } else {
                //如果发现玩家的赛季大于服的赛季，说明是经历了 20210625改动的，要发奖
                if (!firstHalfSentReward) {
                    sendRewardByStar(true);
                }
                if(!secondHalfSentReward){
                    sendRewardByStar(false);
                    star = game().table.gradePvp.level.getInheritStarByStar(star);
                    // 将继承得星级赋值给战绩
                    present.star = star;
                    present.maxStar = star;
                }
                mySeason = serverSeason;
                firstHalfSentReward = false;
                secondHalfSentReward = false;
                clearData();
                checkSeason();
            }
        }
    }

    private int serverSeason() {
        return serverSeasonStatus().season;
    }

    private GradePvpStatus serverSeasonStatus() {
        return game().pvp.gradePvp.status;
    }

    public void clearData() {
        match = null;
        firstHalfSentReward = false;
        secondHalfSentReward = false;
        gotMap.clear();
        records.clear();
        present.clearMark();
    }

    /**
     * 检测能否挑战
     *
     * @return r
     */
    public GameResult<Void> challengeAvaible(boolean checkPlayTimes) {
        GameResult<Void> r = new GameResult<>();
        if (fighting) {
            return r.fail("正在挑战中");
        }
        if (checkPlayTimes) {
            r = playTimes.available(role, GDFunc.GRADE_PVP);
            if (!r.ok()) {
                return r;
            }
        }
        if (LocalDateTime.now().getHour() >= 23) {
            return r.fail("活动时间为每日0点~23点");
        }
        return r;
    }

    /**
     * 匹配
     * @return EcResult
     */
    public GameResult<Void> match(int skip) {
        // 检测能否挑战
        GameResult<Void> r = challengeAvaible(true);
        if (!r.ok()) {
            return r;
        }
        if (!checkSelfPosition()) {
            return r.fail("阵容不能为空");
        }
        if (match != null) {
            return r.fail("您已经在匹配中，请稍等");
        }
        GradePvpPlayerData opponent = game().pvp.gradePvp.match(role.getUid(), star);
        if (opponent == null || opponent.player == null) {
            return r.fail("暂时无法匹配到对手，请稍后再尝试");
        }
        playTimes.added();
        match = new GradePvpMatchData();
        match.matchTime = System.currentTimeMillis();
        match.ps = opponent.player.ps;
        match.skip = skip == 1;
        match.star = opponent.player.star;
        match.rank = opponent.rank;
        role.sendNow(new GradePvpMatchResp(opponent.player.ps, opponent.player.star, match.matchTime));
        return r;
    }

    /**
     * 检查己方的阵容信息
     *
     * @return true/false
     */
    private boolean checkSelfPosition() {
        Position position = role.getFight().getPosition(GDFunc.GRADE_PVP);
        return position != null && !position.grids.isEmpty();
    }

    /**
     * 检查对手的阵容信息
     * @return true/false
     */
    public boolean checkOppoPosition() {
        return match != null && match.ps != null;
    }

    /**
     * 挑战
     *
     * @return EcResult
     */
    public GameResult<Void> challenge() {
        GameResult<Void> r = new GameResult<>();
        if (!isOpen()) {
            return r.fail("功能未开启");
        }
        r = challengeAvaible(false);
        if (!r.ok()) {
            return r;
        }
        if (!checkSelfPosition()) {
            return r.fail("阵容不能为空，请重新设置");
        }
        if (!checkOppoPosition()) {
            return r.fail("对手数据异常，请重新匹配");
        }
        return beginChallenge();
    }

    public void afterChallenge(int oppoStar, int oppoRank, PlayPO play) {
        GradePvpChallengeResp resultResp = new GradePvpChallengeResp();
        resultResp.preStar = star;
        resultResp.preBufferScore = bufferScore;
        resultResp.preScore = score;
        resultResp.win = play.winner == GDFight.Team.A;
        afterChallenge(resultResp.win, oppoStar, oppoRank, play);
        resultResp.star = star;
        resultResp.bufferScore = bufferScore;
        resultResp.score = score;
        List<String> rewardList = game().table.gradePvp.prize.randomRewardByTypeAndResult(
                        resultResp.win, resultResp.preStar);
        MixRes reward = new MixRes();
        for (String rewardStr : rewardList) {
            reward.addList(new MixRes(rewardStr));
        }
        resultResp.reward = reward;
        resultResp.reward.add(role, true);
        role.sendNow(resultResp);
    }

    public GameResult<Void> beginChallenge() {
        GameResult<Void> r = new GameResult<>();
        int oppoStar = match.star;
        int oppoRank = match.rank;
        PlaySide oppoPs = match.ps;
        match = null;
        GradePvpFTC context = new GradePvpFTC(role, oppoStar, oppoRank);
        context.funcId = GDFunc.GRADE_PVP;
        // 处理战斗逻辑
        EcResult<PlayPO> fightRes = role.getGame().play.startByRole(role, context, role.getGame().fight.side.createFightSideByPlaySide(GDFight.Team.B, oppoPs));
        if (!fightRes.ok()) {
            return r.fail(fightRes.message);
        }
        fighting = true;
        return r;
    }

    private void afterChallenge(boolean win, int oppoStar, int oppoRank, PlayPO play) {
        int preStar = star;
        GradePvpLevelRow level = game().table.gradePvp.level.getRowByStar(preStar);
        // 常规赛才会影响段位
        int tmpBufferScore = bufferScore;
        // 星级和积分的逻辑处理
        int tmpStar = preStar;
        if (win) {
            tmpStar += 1;
            tmpBufferScore += level.winBufferScore;
        } else {
            if (preStar > GDGradePvp.KEEP_STAR_MAX) {
                if (tmpBufferScore > GDGradePvp.BUFFER_SCORE_PREVENT) {
                    tmpBufferScore -= GDGradePvp.BUFFER_SCORE_PREVENT;
                } else {
                    tmpStar -= 1;
                }
            }
            tmpBufferScore += level.loseBufferScore;
        }
        tmpStar = Math.max(tmpStar, 0);
        tmpBufferScore = Math.min(tmpBufferScore, GDGradePvp.BUFFER_SCORE_MAX);
        star = tmpStar;
        bufferScore = tmpBufferScore;
        score += calcScore(win);
        score = Math.max(0, score);
        if (play != null) {
            updateMark(win, play);
            GradePvpRecord record = createRecordPO(preStar, oppoStar, myRank, oppoRank, play);
            try{
                ReplaySaveReq saveReq = ReplayUtils.toReplaySaveReq(play,
                        play.expiredDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
                EcResult<ReplaySaveResp> saveRes = game().openApi().saveReplay(saveReq);
                if (saveRes.ok()) {
                    record.replayUid = saveRes.data.getId();
                }
                game().pvp.gradePvp.fightOver(play.a, star, score, record);
            } catch (Exception e) {
                logger.error("段位赛保存录像出错", e);
            }
            handleChallengeResult(record);
        }
    }

    private int calcScore(boolean win) {
        if (win) {
            int extraScore = present.curSerialWinTimes > 0 ? (present.curSerialWinTimes - 1) * GDGradePvp.SERIAL_WIN_GRADE_SCORE : 0;
            return Math.min(GDGradePvp.WIN_GRADE_SCORE + extraScore, GDGradePvp.MAX_WIN_GRADE_SCORE);
        }
        int curSerialLoseTimes =  Math.min(present.curSerialLoseTimes, GDGradePvp.MAX_SERIAL_LOSE_TIMES);
        int extraScore = curSerialLoseTimes > 0 ? (curSerialLoseTimes - 1) * GDGradePvp.SERIAL_WIN_GRADE_SCORE : 0;
        return - Math.min(GDGradePvp.LOSE_GRADE_SCORE - extraScore, GDGradePvp.MAX_LOSE_GRADE_SCORE);
    }

    public GradePvpRecord createRecordPO(int star, int oppoStar, int rank, int oppoRank, PlayPO play) {
        return new GradePvpRecord(
                play.a.snapshot.base,
                play.b.snapshot.base,
                System.currentTimeMillis(),
                play.winner,
                0L,
                star,
                oppoStar,
                rank,
                oppoRank
                );
    }

    /**
     * 领取段位奖励
     *
     * @param tid 奖励id
     */
    public GameResult<Void> getReward(int tid) {
        GameResult<Void> r = new GameResult<>();
        GradePvpRewardRow rewardRow = game().table.gradePvp.reward.get(tid);
        if (rewardRow == null) {
            return r.fail("奖励不存在！");
        }
        if (present.maxStar < rewardRow.star) {
            return r.fail("无法领取该奖励！");
        }
        if (gotMap.containsKey(tid)) {
            return r.fail("奖励已领取！");
        }
        gotMap.put(tid, true);
        new MixResAdder()
                .setDialogReward(true)
                .setSendMailWhenFull(true)
                .setRes(rewardRow.firstRewardStr)
                .exe(role);
        noticeUpdate();
        return r;
    }

    public void handleChallengeResult(GradePvpRecord record) {
        synchronized (records) {
            if (records.size() >= GDGradePvp.MASTER_RECORD_NUM_MAX) {
                records.remove(records.size() - 1);
            }
            records.add(0, record);
        }
    }

    @Override
    public void onDaySpan(boolean silence) {
        playTimes.reset();
        noticeUpdate();
    }

    public void noticeUpdate() {
        role.sendNow(new GradePvpDataResp(this));
    }

    @Override
    public int getFightFuncId() {
        return getFuncId();
    }

    // 更新战绩
    public void updateMark(boolean win, PlayPO play) {
        if (win) {
            // 连胜次数+1
            present.winTimes += 1;
            total.winTimes += 1;
            present.curSerialWinTimes += 1;
            present.curSerialLoseTimes = 0;
        } else {
            present.curSerialWinTimes = 0;
            present.curSerialLoseTimes += 1;
        }
        present.star = star;
        total.star = star;
        present.fightTimes += 1;
        total.fightTimes += 1;
        if (present.curSerialWinTimes > present.serialWinTimes) {
            present.serialWinTimes = present.curSerialWinTimes;
        }
        // 连胜次数
        if (present.serialWinTimes > total.serialWinTimes) {
            total.serialWinTimes = present.serialWinTimes;
        }
        // 星级
        if (present.star >= present.maxStar) {
            present.maxStar = present.star;
            if (present.maxStar >= total.maxStar) {
                total.maxStar = present.maxStar;
            }
        }
        // 最大伤害
        int mvpHeroTid = 0;
        long mvpHeroDamage = 0, totalDamage = 0;
        for (HeroFighter hero : play.teamEnd.a.getAllHero()) {
            totalDamage += hero.performance.damage;
            if (hero.performance.damage > mvpHeroDamage) {
                mvpHeroTid = hero.heroTid;
                mvpHeroDamage = hero.performance.damage;
            }
        }
        if (totalDamage >= present.maxDamage) {
            present.maxDamage = totalDamage;
            if (present.maxDamage >= total.maxDamage) {
                total.maxDamage = present.maxDamage;
            }
        }
        if (mvpHeroTid != 0) {
            // mvp英雄
            present.mvpHero.putIfAbsent(mvpHeroTid, 0);
            present.mvpHero.put(mvpHeroTid, present.mvpHero.get(mvpHeroTid) + 1);
            total.mvpHero.putIfAbsent(mvpHeroTid, 0);
            total.mvpHero.put(mvpHeroTid, total.mvpHero.get(mvpHeroTid) + 1);
        }
    }
}
